package XPathProj;
import java.io.IOException;


import org.w3c.dom.*;
import org.xml.sax.SAXException;

import com.sun.org.apache.xml.internal.utils.QName;

import javax.xml.parsers.*;
import javax.xml.xpath.*;


import java.io.StringWriter;
import java.util.List;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

public class XPathExample {
	public static void main(String[] args)
		throws ParserConfigurationException, SAXException,
		IOException, XPathExpressionException {
			//load the document into a DOM Document
			DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
			domFactory.setNamespaceAware(true); // never forget this!
			DocumentBuilder builder = domFactory.newDocumentBuilder();
			Document doc = builder.parse("books.xml");
			//create an XPath factory
			XPathFactory factory = XPathFactory.newInstance();
			//create an XPath Object
			XPath xpath = factory.newXPath();
			
			Object resultNODE;
			Object resultBOOL;
			Object resultNUM;
//			NodeList nodes;
			
			XPathProjectImplementation obj = new XPathProjectImplementation();
			obj.loadXML("books.xml");
			
//			System.out.println("find position's result: " + obj.replacePositionWithValue("position() + 1 + count(chapter[position()]/section[position()>1]) + 2 + position() + 3 + (position())", 4));
//			System.out.println("find position's result: " + obj.findPosition("position() + 1 + count(chapter) + 2 + position()", 4));
			
//			Object result = obj.evaluate("/inventory/book[3]/preceding-sibling::book[1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[chapter[section]]", XPathConstants.NODESET);			
//			Object result = obj.evaluate("/inventory/book[3] | /inventory/book[2]", XPathConstants.NODESET); // HANDLE SPACES
//			Object result = obj.evaluate("/inventory/book[chapter[section[paragraph[image]]]]/chapter/section/paragraph[image]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not((price>14 and price<2) or price=7.50)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[(price>14 and price<2) or price=7.50]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(price>10 or price<1)][chapter[title]][2]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[price > 5]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[2]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/i)nventory/book[@num]", XPathConstants.NODESET);
//			Object result = obj.evaluate("inventory/book[not(not(chapter[section]))] | /inventory/book[last()]", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("/inventory/book[chapter][not(last())][2]", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("/inventory/book/price[.>5]", XPathConstants.NODESET);
//			Object result = obj.evaluate("id(//@num)[..//price[. = .] > ./price]", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("/inventory/book/chapter[paragraph][last()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book/.[price]", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("/inventory/book[5<price]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book/price[self::node()/self::node()/text()>0]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/family/person[\"female\"=id(@children)/@gender]", XPathConstants.NODESET); // TODO if starts with "id(", remove the "/"
//			Object result = obj.evaluate("/family/person[id(@children)/@gender]", XPathConstants.NODESET);			
//			Object result = obj.evaluate("/family/person/@children", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book/price/self::node()[5<text()]", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("//inventory/./book/price/self::node()[5<text()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[3]/preceding-sibling::book", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("(/inventory/book/chapter)[last()]", XPathConstants.NODESET);
			
//			Object result = obj.evaluate("/family/person[@gender=\"male\"][2]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/family/person[@gender=\"male\"][last()]/@father", XPathConstants.NODESET);
//			
//			Object result = obj.evaluate("/inventory/book[not(not(not(priceor | chapter/sectio)))]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[orbach or (chapter/section | price[self::node()>5]) or (ofri or chapter) or (price>5)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[(price=4.99) or (price>5)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book/chapter[last()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/family/person[id(@children)/@gender = id(@children)/@gender]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/family/person[id(@children)/@gender]", XPathConstants.NODESET);
//			Node source = (Node) result1;
//			Object result = obj.evaluate("id(@children)", source, XPathConstants.NODESET);
			
//			Object result = obj.evaluate("/inventory/book[count(chapter)]", XPathConstants.NODESET); // TODO //author[last()]
//			Object result = obj.evaluate("/inventory/book[position() > 1][position()=1 or count(chapter)=1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[(price>14 and price<2) or price=7.50]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book//(chapter)[last()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("//author[last()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[chapter/title][position() > 1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[count(chapter)=1][1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/(book/(chapter)[1])[2]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[2]/author/preceding::author[1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[count(author)>=2]/preceding::author[2]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[count(author)>=2][true()][1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("//self::node()[price>5]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[count(author)>=2]/preceding::author[1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("id(\"b1 b2\")[//chapter][last()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/family/person/id(@children)[id(@children)]", XPathConstants.NODESET);			
//			Object result = obj.evaluate("/family/person/id(@children)[id(@children)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[count(price)!=3][1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("(/inventory[(not((book/price) < (book/price)))])", XPathConstants.NODESET);
//			Object result = obj.evaluate("/family/person[id(@children)[not(@gender = \"female\")]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("(/inventory[(not((book/price) < (book/price)))])", XPathConstants.NODESET);			
//			Object result = obj.evaluate("/inventory/book/price[0 < self::node()/self::node()/text()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(self::node()[price<0] | chapter/section)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(1 > 1)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(chapter/section and price<0)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[5 < id(@num)/price][1.5]", XPathConstants.NODESET);
//			Object result = obj.evaluate("id(\"b1 b2\")", XPathConstants.NODESET);			
//			Object result = obj.evaluate("/inventory/book[chapter][not(id(@num)/price > 15)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[chapter][not(self::node()/price < id(@num)/price)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[chapter][not(id(@num)/price > self::node()/price)][(((last())))]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[chapter][not(id(@num)/price < id(@num)/price)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[(chapter)][false()][position() > 1]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(false())]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/(book[chapter])[2]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(15 < self::node()/price)][(not(last()))]", XPathConstants.NODESET); // TODO not(last())
//			Object result = obj.evaluate("/inventory/book[not(chapter/price)][not(last())]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[last()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(not(chapter))]", XPathConstants.NODESET);

//			Object result = obj.evaluate("/inventory/book[5 < (id(@num)/price)[1]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[\"usd\" = (price/@currency)[1]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[price/text() > 5]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[5 < (self::node()/price)[1]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book/chapter[2]/descendant::node()", XPathConstants.NODESET);

//			Object result = obj.evaluate("/inventory/book[5 < (id(@num)/price)[1]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[\"usd\" = (price/@currency)[1]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[self::node() =  .]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[not(price>5)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[ (not(price > 5)) ]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[price[text()>3]]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[(position()=1)]", XPathConstants.NODESET);
//			Object result = obj.evaluate("(/inventory/book/preceding-sibling::book)[true()]", XPathConstants.NODESET);
//			Object result = obj.evaluate("/inventory/book[5 < (id(@num)/price)/self::node()]", XPathConstants.NODESET);			
			Object result = obj.evaluate("/inventory/book[count(author) = 1][position() = 1]", XPathConstants.NODESET);
			
			// /family/person/id(@children)
			
//			Object result =  obj.evaluate("/inventory/book[3]/preceding-sibling::book[1][price = 5.99]", XPathConstants.BOOLEAN);			
//			boolean bool = (boolean) result;
//			System.out.println(bool);
			
//			Object result =  obj.evaluate("(/inventory/book/price)*2 + 5 - 2*(/inventory/book[2]/price[1]) div 2 - 2", XPathConstants.NUMBER);
//			double number = (double) result;
//			System.out.println(number);
			
//			Object result =  obj.evaluate("/inventory/book[2]/author[1]", XPathConstants.STRING);
//			String string = (String) result;
//			System.out.println(string);
			
			// TODO only condition- chapter[] <-- no slashes!
			
//			String[] tokens = exp.split(">|=|<");
			
//			//1st option - compile and run
//			//****************************//
//			//make the XPath object compile the XPath expression
//			XPathExpression expr = xpath.compile("inventory");
						
//			XPathExpression expr = xpath.compile("/inventory/book[id(@num)]");
			
			// /inventory/book{1}[id(@num{"b1"})]" self::node()/@nu
			
//			XPathExpression expr = xpath.compile("/inventory/book[not(price>6)]");
//			XPathExpression expr = xpath.compile("/inventory/book[5 < self::node()/price[@currency][1]]");
//			XPathExpression expr = xpath.compile("/inventory/book[5 < id(@num)/price][1.5]");
//			XPathExpression expr = xpath.compile("(/inventory/book/price)*2 + 5 - 2*(/inventory/book[2]/price[1]) div 2 + 2");
//			XPathExpression expr = xpath.compile("/inventory/book[count(chapter[position()])>1][position()=2]");
//			XPathExpression expr = xpath.compile("/inventory/book[count(chapter)=3][position() = 1]");
//			XPathExpression expr = xpath.compile("/inventory/book[3]/preceding-sibling::book[chapter/section]");			
//			XPathExpression expr = xpath.compile("/inventory/book[3]/price/preceding-sibling::node()");
//			XPathExpression expr = xpath.compile("/inventory/book[price/text()>5]");
//			XPathExpression expr = xpath.compile("/inventory/book[chapter or price]");
			
//			XPathExpression expr = xpath.compile("/inventory/book/price/text()/parent::node()");
//			XPathExpression expr = xpath.compile("/family/person[id(@children)[2]/@gender=\"male\"]");
//			XPathExpression expr = xpath.compile("/family/person[not(price>6)]");
			
//			XPathExpression expr = xpath.compile("/inventory/book/chapter[paragraph][last()]");
//			XPathExpression expr = xpath.compile("/family/person[id(/family/person/@idnum)/@gender = \"female\"]");			
			
			//evaluate the XPath expression
//			Object result = expr.evaluate(doc, XPathConstants.NODESET);
						
//			Object result = expr.evaluate(doc, XPathConstants.NUMBER);
//			System.out.println(result);
			
//			Object result = expr.evaluate(doc, XPathConstants.STRING);
//			System.out.println(result);
			
//			System.out.println(result);
			
//			Object result1 = xpath.evaluate("/inventory/book[1]",doc,XPathConstants.NODE);
//			System.out.println("!!!!!");
//			nodeToString((Node)result1);
//			System.out.println("!!!!!");
//			Object result = xpath.evaluate("/book/price", ((Node)(result1)),XPathConstants.NODESET);
			NodeList nodes = (NodeList) result;
			
			//print the output
			System.out.println("1st option:");
			for (int i = 0; i < nodes.getLength(); i++) {
				System.out.println("i: " + i);
				System.out.println("*******");
				System.out.println(nodeToString(nodes.item(i)));
				System.out.println("*******");
				
				//
//				System.out.println("node text: " + nodes.item(i).getTextContent());
//				System.out.println("node value: " + nodes.item(i).getNodeValue());
				//
			}
			
			for (int i = 0; i < nodes.getLength(); i++) {
				System.out.println("i: " + i);
				System.out.println("*******");
				System.out.println(((XpathProjectNodeList)(result)).itemPath(i));
				System.out.println("*******");
			}
//			
//			
//			//2nd option - evaluate an XPath expression without compiling
//			//***********************************************************//
//			Object result2 = xpath.evaluate("/inventory/book[3]/preceding-sibling::book[1]",doc,XPathConstants.NODESET);
//			System.out.println("2nd option:");
//			NodeList nodes = (NodeList) result2;
//			//print the output
//			for (int i = 0; i < nodes.getLength(); i++) {
//				System.out.println("i: " + i);
//				System.out.println("*******");
//				System.out.println(nodeToString(nodes.item(i)));
//				System.out.println("*******");
//			}
//			
//			
//			//3rd option - evaluate an XPath expression without compiling
//			//running on output of an expression
//			//***********************************************************//
//			Object result3 = xpath.evaluate("inventory/book[position()>=2]",doc,XPathConstants.NODESET);
//			System.out.println("3rd option:");
//			NodeList nodes = (NodeList) result3;
//			for (int i = 0; i < nodes.getLength(); i++) {
//				System.out.println("i: " + i);
//				System.out.println("///////");
//				result3 = xpath.evaluate("author/text()",nodes.item(i),XPathConstants.NODESET);
//				NodeList nodes2 = (NodeList) result3;
//				for (int j = 0; j < nodes2.getLength(); j++) {
//					System.out.println("j: " + j);
//					System.out.println("*******");
//					System.out.println(nodes2.item(j).getNodeValue());
//					System.out.println("*******");
//				}
//				System.out.println("///////");
//			}
//			
//			
//			//creating a NodeList from several answers
//			//********************************************//
//			Object result1 = xpath.evaluate("/inventory/book[3]",doc,XPathConstants.NODE);
//			Object result2 = xpath.evaluate("/inventory/book[1]",doc,XPathConstants.NODE);
//			
//			
//
//			DocumentBuilder newBuilder = domFactory.newDocumentBuilder();
//			Document newDoc = newBuilder.newDocument();
//			
//			Element root = newDoc.createElement("root");
//			newDoc.appendChild(root);
//			root.appendChild(newDoc.importNode((Node)result1, true));
//			root.appendChild(newDoc.importNode((Node)result2, true));
//			
//			NodeList nodes = root.getChildNodes();
//			
//			for (int j = 0; j < nodes.getLength(); j++) {
//				System.out.println("j: " + j);
//				System.out.println("*******");
//				System.out.println(nodeToString(nodes.item(j)));
//				System.out.println("*******");
//			}
//			
			//examples for the check:
//			resultNUM = impl.evaluate("count(/inventory/book)",XPathConstants.NUMBER);
//			System.out.println((Double)resultNUM);
			
//			resultNODE = impl.evaluate("/inventory/book[3]/price/preceding-sibling::node()", XPathConstants.NODESET);
//			System.out.println(nodeToString(nodes.item(1)));
			
//			resultNODE = impl.evaluate("/inventory/book[3]/price/preceding-sibling::node()", XPathConstants.NODE);
//			System.out.println(nodeToString((Node)resultNODE));
			
//			resultBOOL = impl.evaluate("//image/@width>50",XPathConstants.BOOLEAN);
//			System.out.println((Boolean)resultBOOL);
			
			
		}
	
	/*private*/ public static String nodeToString(Node node) {
		StringWriter sw = new StringWriter();
		try {
		Transformer t = TransformerFactory.newInstance().newTransformer();
		t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
		t.transform(new DOMSource(node), new StreamResult(sw));
		} catch (TransformerException te) {
		System.out.println("nodeToString Transformer Exception");
		}
		return sw.toString();
		}
	}